# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Graphql::Aggregations::VulnerabilityStatistics::LazyAggregate do
  let(:query_ctx) do
    {}
  end

  let_it_be(:vulnerable) { create(:group) }
  let_it_be(:other_vulnerable) { create(:group) }

  let(:include_subgroups) { true }

  describe '#initialize' do
    let(:filter) { nil }

    subject(:lazy_aggregator) { described_class.new(query_ctx, vulnerable, include_subgroups: true, filter: filter) }

    it 'adds the vulnerable to the lazy state' do
      expect(lazy_aggregator.lazy_state[:pending_vulnerables]).to eq({ [true, nil] => Set.new([vulnerable]) })
      expect(lazy_aggregator.vulnerable).to match vulnerable
    end

    context 'when the filter is given' do
      let(:filter) { :a }

      it 'adds the vulnerable to the lazy state by given filter' do
        expect(lazy_aggregator.lazy_state[:pending_vulnerables]).to eq({ [true, :a] => Set.new([vulnerable]) })
        expect(lazy_aggregator.vulnerable).to match vulnerable
      end
    end
  end

  describe '#execute' do
    let(:fake_data) do
      {
        vulnerable => [::Vulnerabilities::ProjectsGrade.new(vulnerable, 'a', [])],
        other_vulnerable => [::Vulnerabilities::ProjectsGrade.new(other_vulnerable, 'b', [])]
      }
    end

    subject { described_class.new(query_ctx, vulnerable, include_subgroups: include_subgroups) }

    before do
      subject.instance_variable_set(:@lazy_state, fake_state)
      allow(::Vulnerabilities::ProjectsGrade).to receive(:grades_for).and_return(fake_data)
    end

    context 'if the record has already been loaded' do
      let(:fake_state) do
        { pending_vulnerables: {}, loaded_objects: { [true, nil] => { vulnerable => ::Vulnerabilities::ProjectsGrade.new(vulnerable, 'a', []) } } }
      end

      it 'does not make the query again' do
        expect(::Vulnerabilities::ProjectsGrade).not_to receive(:grades_for)

        subject.execute
      end
    end

    context 'if the record has not been loaded' do
      let(:include_subgroups) { true }
      let(:fake_state) do
        {
          pending_vulnerables: { [true, nil] => Set.new([vulnerable]), [false, nil] => Set.new([other_vulnerable]) },
          loaded_objects: Hash.new { |h, k| h[k] = {} }
        }
      end

      it 'makes the query' do
        expect(::Vulnerabilities::ProjectsGrade).to receive(:grades_for).with([vulnerable], filter: nil, include_subgroups: true)
        expect(::Vulnerabilities::ProjectsGrade).to receive(:grades_for).with([other_vulnerable], filter: nil, include_subgroups: false)

        subject.execute
      end

      it 'clears the pending IDs' do
        subject.execute

        expect(subject.lazy_state[:pending_vulnerables].values).to all(be_empty)
      end
    end
  end
end
